package com.peas.platform.common.core.configuration;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import feign.Client;
import feign.Request;
import feign.RequestTemplate;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.http.HttpHeaders;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;

@ConditionalOnNacosDiscoveryEnabled
public class CustomFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    private NacosDiscoveryProperties discoveryProperties;
    @Resource
    private NacosServiceManager nacosServiceManager;

    public CustomFeignBlockingLoadBalancerClient(Client delegate,
                                                 LoadBalancerClient loadBalancerClient,
                                                 LoadBalancerClientFactory loadBalancerClientFactory) {
        super(delegate, loadBalancerClient, loadBalancerClientFactory);
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        RequestTemplate template = request.requestTemplate();
        URI uri = URI.create(template.url());
        String clientName = template.feignTarget().name();
        if (StrUtil.isBlank(clientName)) {
            throw new RuntimeException("Service name [" + clientName + "] cannot be empty.");
        }
        if (isServerless(clientName)) {
            log.info("{} is serverless.", clientName);
            Instance instance = getServerlessInstance(clientName);
            String path = uri.getPath();
            String ip = System.getenv("SERVERLESS_LOADBALANCER_IP");
            int port = instance.getPort();
            Assert.notBlank(ip, "环境变量SERVERLESS_LOADBALANCER_IP为空。");
            String fullUrl = uri.getScheme() + "://" + ip + ":" + port + path;
            Client client = new Default(null, null);
            template.header(HttpHeaders.HOST, instance.getIp());
            log.debug(template.headers().toString());
            Request req = Request.create(request.httpMethod(), fullUrl, template.headers(), template.body(), template.requestCharset(), template);
            Response resp = client.execute(req, options);
            log.debug(resp.toString());
            return resp;
        } else {
            log.debug("{} is not serverless.", clientName);
            return super.execute(request, options);
        }
    }

    private boolean isServerless(String serviceName) {
        List<Instance> instances;
        try {
            instances = nacosServiceManager.getNamingService().getAllInstances(serviceName);
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
        Assert.notEmpty(instances, "找不到实例：" + serviceName + ",cluster:" + discoveryProperties.getClusterName() + ",namespace:" + discoveryProperties.getNamespace() + ",group:" + discoveryProperties.getGroup());
        for (Instance instance : instances) {
            Map<String, String> map = instance.getMetadata();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                if (entry.getKey().equalsIgnoreCase("serverless") && entry.getValue().equalsIgnoreCase("true")) {
                    return true;
                }
            }
        }
        return false;
    }

    private Instance getServerlessInstance(String serviceName) {
        List<Instance> instances;
        try {
            instances = nacosServiceManager.getNamingService().getAllInstances(serviceName);
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
        Assert.notEmpty(instances, "找不到实例");
        return instances.get(0);
    }

}
